#include "shader.h"

#include "checkError.h"
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>

Shader::Shader(const char *vertPath, const char *fragPath) {
  std::string vertexCode;
  std::string fragmentCode;

  // 1. 编译Shader代码
  std::string vertSrc = readShaderFile(vertPath);
  GLuint vert = compileShader(vertSrc, GL_VERTEX_SHADER);
  std::string fragSrc = readShaderFile(fragPath);
  GLuint frag = compileShader(fragSrc, GL_FRAGMENT_SHADER);

  // 2. 创建一个Program壳子
  mProgram = glCreateProgram();

  // 3. 将编译结果放入壳子中
  glAttachShader(mProgram, vert);
  glAttachShader(mProgram, frag);
  // 4. 执行link操作
  glLinkProgram(mProgram);
  {
    int success = false;
    char infoLog[1024];
    int logSize = 1024;
    glGetProgramiv(mProgram, GL_LINK_STATUS, &success);
    if (!success) {
      glGetProgramInfoLog(mProgram, logSize, NULL, infoLog);
      std::cout << "Error: SHADER LINK ERROR\n" << std::endl;
    }
  }

  // 5. 清理
  glDeleteShader(vert);
  glDeleteShader(frag);
}

Shader::~Shader() {}

void Shader::begin() { GL_CALL(glUseProgram(mProgram)); }

void Shader::end() { GL_CALL(glUseProgram(0)); }

void Shader::setFloat(const std::string &name, float value) {
  // 1. 通过名称拿到Uniform变量的位置Location
  GLint location = GL_CALL(glGetUniformLocation(mProgram, name.c_str()));

  // 2. 通过Location更新Uniform变量的值
  GL_CALL(glUniform1f(location, value));
}

void Shader::setVector3(const std::string &name, float x, float y, float z) {
  // 1. 通过名称拿到Uniform变量的位置Location
  GLint location = GL_CALL(glGetUniformLocation(mProgram, name.c_str()));
  // 2. 通过Location更新Uniform变量的值
  GL_CALL(glUniform3f(location, x, y, z));
}

void Shader::setVector3(const std::string &name, const float *values) {
  // 1. 通过名称拿到Uniform变量的位置Location
  GLint location = GL_CALL(glGetUniformLocation(mProgram, name.c_str()));
  // 2. 通过Location更新Uniform变量的值
  GL_CALL(glUniform3fv(location, 1, values));
}

void Shader::setInt(const std::string &name, int value) {
  // 1. 通过Location更新Uniform变量的值
  GLint location = GL_CALL(glGetUniformLocation(mProgram, name.c_str()));
  // 2. 通过Location更新Uniform变量的值
  GL_CALL(glUniform1i(location, value));
}

std::string Shader::readShaderFile(const char *filePath) {
  // 声明装入shader代码的字符串
  std::string content;
  // 声明用于读取文件的inFileStream
  std::ifstream fileStream;

  // 保证ifsteam遇到问题可以抛出异常
  fileStream.exceptions(std::ifstream::failbit | std::ifstream::badbit);
  try {
    // 1. 打开文件
    fileStream.open(filePath);
    // 2. 将文件输入流中的字符串输入到stringStream中
    std::stringstream shaderStream;
    shaderStream << fileStream.rdbuf();
    // 3. 关闭文件
    fileStream.close();
    // 4. 将字符串类型进行转换
    content = shaderStream.str();

  } catch (std::ifstream::failure &e) {
    std::cout << "ERROR: Shader File Error: " << e.what() << std::endl;
  }

  return content;
}

GLuint Shader::compileShader(std::string shaderSrc, GLenum shaderType) {
  const char *cshaderSrc = shaderSrc.c_str();

  // 1. 创建Shader程序
  GLuint shader;
  shader = glCreateShader(shaderType);
  // 2. 为Shader程序输入Shader代码
  glShaderSource(shader, 1, &cshaderSrc, NULL);
  // 3. 编译Shader代码
  glCompileShader(shader);
  {
    // 错误处理
    int success = false;
    glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
    char infoLog[1024];
    int infoSize = 1024;
    if (!success) {
      glGetShaderInfoLog(shader, infoSize, NULL, infoLog);
      std::cout << "Error: SHADER COMPILE ERROR - " << shaderType << std::endl;
    }
  }
  return shader;
}
